<?php

class APF_Exception extends Exception
{
    private $lang_key = '';
    private $msg = '';
    private $mod_name = '';
    private $cause = null;
    private $errorNo = 0;

    /**
     * 构造函数。支持重载。
     *
     * 一)一个参数，调用以下接口：
     *       @param string $lang_key
     *       __construct($lang_key)
     *
     * 二)两个参数，
     *      如果第二参数是cls_exception对象，调用以下接口：
     *      @param string $lang_key
     *      @param cls_exception $cause
     *       __construct($lang_key, $cause)
     *
     *      如果第二参数是数字型，调用以下接口：
     *      @param string $lang_key
     *      @param cls_exception $errorNo
     *       __construct($lang_key, $errorNo)
     *
     *      如果第二参数是string，调用以下接口：
     *      @param string $lang_key
     *      @param string $mod_name
     *       __construct($lang_key, $mod_name)
     *
     * 三)三个参数，调用以下接口：
     *      如果第二参数是字符串，调用以下接口：
     *      @param string $lang_key
     *      @param string $mod_name
     *      @param string $cause:
     *       __construct($lang_key, $mod_name, $cause)
     *
     *      如果第二参数是数字型，调用以下接口：
     *      @param string $lang_key
     *      @param string $errorNo
     *      @param string $cause:
     *       __construct($lang_key, $errorNo, $cause)
     *
     * 四)四个参数，调用以下接口：
     *      @param string $lang_key
     *      @param string $mod_name
     *      @param string $cause
     *      @param string $numeric
     *      __construct($lang_key, $mod_name, $cause, $errorNo)
     */
    public function __construct()
    {
        switch (func_num_args())
        {
            case 1 :
                $this->lang_key = func_get_arg(0);
                break;

            case 2 :
                $this->lang_key = func_get_arg(0);
                $o = func_get_arg(1);
                if (is_string($o))
                {
                    $this->mod_name = $o;
                }
                else if ($o instanceof Exception)
                {
                    $this->cause = $o;
                }
                else if (is_numeric($o))
                {
                    $this->errorNo = $o;
                }
                break;

            case 3 :
                $this->lang_key = func_get_arg(0);
                $o = func_get_arg(1);
                if (is_numeric($o))
                {
                    $this->errorNo = $o;
                }
                else
                {
                    $this->mod_name = $o;
                }
                $this->cause = func_get_arg(2);
                break;

            case 4 :
                $this->lang_key = func_get_arg(0);
                $this->mod_name = func_get_arg(1);
                $this->cause = func_get_arg(2);
                $this->errorNo = func_get_arg(3);
                break;

            default :
                die('exception::__construct()/Unexpected argument number: ' . func_num_args());
                break;
        }

        if (!$this->mod_name)
        {
            $file_path = $this->getFile();
            if (($end = strrpos($file_path, '.class')) !== false)
            {
                $start = strrpos($file_path, '/') + 1;
                $this->mod_name = substr($file_path, $start, $end - $start);
            }
        }

        if (is_array($this->lang_key))
        {
            $this->msg = implode('|', $msg_parts);
        }
        else
        {
            $this->msg = $this->lang_key;
        }

        if (!$this->msg)
        {
            $this->msg = $this->lang_key;
        }

        $this->message = $this->msg;
    }

    /**
     * 获取这个异常的上个异常(在同一个异常链上)
     *
     * @return cls_exception instance
     */
    public function get_cause()
    {
        return $this->cause;
    }

    /**
     * 获取本次异常的语言项
     *
     * @return string
     */
    public function get_lang_key()
    {
        return $this->lang_key;
    }

    /**
     * 获取异常链上所有的语言项。注意：返回的数据是一个FIFO的队列。
     *
     * @return array
     */
    public function get_lang_keys()
    {
        $lang_keys = array();
        $e = $this;
        do
        {
            $lang_keys[] = $e->get_lang_key();
            $e = $e->get_cause();
        }
        while ($e);

        return array_reverse($lang_keys);
    }

    /**
     * 获得本异常附带的errorNo。
     *
     * @return numeric
     */
    public function getErrorNo()
    {
        return $this->errorNo;
    }

    /**
     * 获得本次异常本地化的消息
     *
     * @return array
     */
    public function get_msg()
    {
        return $this->msg;
    }

   /**
     * 获取异常链上所有的本地化的异常消息。注意：返回的数据是一个FIFO的队列。
     *
     * @return array
     */
    public function get_msgs()
    {
        $msgs = array();
        $e = $this;
        do
        {
            $msgs[] = $e->get_msg();
            $e = $e->get_cause();
        }
        while ($e);

        return array_reverse($msgs);
    }

    /**
     * 获得本次异常所属的模块名
     *
     * @return string
     */
    public function get_mod_name()
    {
        return $this->mod_name;
    }

    /**
     * 获得异常轨迹
     *
     * @return array
     */
    public function get_stack_trace()
    {
        $info = array();

        $i = 0;
        $e = $this;
        do
        {
            $info[$i]['lang_key'] = $e->get_lang_key();
            $info[$i]['msg'] = $e->get_msg();
            $info[$i]['mod_name'] = $e->get_mod_name();
            $info[$i]['thrown_at'] = 'thrown at ['.$e->getFile().':'.$e->getLine().']';
            $info[$i]['trace'] = $e->getTrace();

            $i++;
            $e = $e->get_cause();
        }
        while ($e);

        return $info;
    }

    /**
     * 格式化异常轨迹
     *
     * @param string $lf:"\r\n"
     * @return string
     */
    public function format_stack_trace($lf = "\r\n")
    {
        $string = '';

        $info = $this->get_stack_trace();
        $i = 0;
        foreach ($info as $item)
        {
            if ($i++ > 0)
            {
                $string .= "Caused by: ";
            }

            $at = empty($item['mod_name']) ? '' : '@';
            if ($item['msg'] != $item['lang_key'] || !empty($item['mod_name']))
            {
                $ld = '(';
                $rd = ')';
            }
            else
            {
                $ld = '';
                $rd = '';
            }

            if ($item['msg'] == $item['lang_key'])
            {
                $item['msg'] = '';
            }

            $string .= "cls_exception: {$item['lang_key']}{$ld}"
                . "{$item['msg']}{$at}{$item['mod_name']}{$rd}$lf";

            $j = 0;

            $string .= "\t\t#{$j} {$item['thrown_at']}$lf";
            $j++;

            foreach ($item['trace'] as $this_trace)
            {
                $class = empty($this_trace['class']) ? '' : $this_trace['class'];
                $type = empty($this_trace['type']) ? '' : $this_trace['type'];

                $args = '';
                $k = 0;
                if (!empty($this_trace['args']))
                {
                    foreach ($this_trace['args'] as $arg)
                    {
                        if (is_array($arg))
                        {
                            $args .= 'Array';
                        }
                        elseif (is_object($arg))
                        {
                            $args .= 'Object';
                        }
                        else
                        {
                            $args .= $arg;
                        }

                        if ($k++ < count($this_trace['args']) - 1)
                        {
                            $args .= ', ';
                        }
                    }
                }

                $position = empty($this_trace['file'])
                    ? ''
                    : "called at [{$this_trace['file']}:{$this_trace['line']}]";

                $string .= "\t\t#{$j} {$class}{$type}{$this_trace['function']}($args) "
                    . "{$position}$lf";

                $j++;
            }
        }

        return $string;
    }

}

?>